<?php
/**
 * Created by Mauro Gadaleta
 * User: Mauro
 * Date: 12/04/11
 * Time: 12:20
 *
 * Autogenerador de forumlarios
 */

class formGeneratorCMS extends gadgetsCMS
{
    /**
     * @var string
     */
    protected $template = 'form.tpl';

    /**
     * @var string
     */
    protected $_action;

    /**
     * @var string
     */
    protected $_method;

    /**
     * @var \Doctrine_Table
     */
    protected $_table;

    /**
     * @var
     */
    protected $_model;

    /**
     * @var
     */
    protected $_elements;

    /**
     * @var array
     */
    protected $_columnTypes = array(
		'integer' => 'text',
		'decimal' => 'text',
		'float' => 'text',
		'string' => 'text',
		'varchar' => 'text',
		'boolean' => 'checkbox',
		'timestamp' => 'text',
		'time' => 'text',
		'date' => 'text',
		'enum' => 'select'
	);

    /**
     * @var array
     */
    protected $defaults = array();

	/**
	 * Array of hooks that are called before saving the column
	 * @var array
	 */
	protected $_columnHooks = array();

	/**
	 * Default validators for doctrine column types
	 * @var array
	 */
	protected $_columnValidators = array(
		'integer' => 'int',
		'float' => 'float',
		'double' => 'float'
	);

	/**
	 * Prefix fields with this
	 * @var string
	 */
	protected $_fieldPrefix = '';

	/**
	 * Column names listed in this array will not be shown in the form
	 * @var array
	 */
	protected $_ignoreColumns = array();

	/**
	 * Whether or not to generate fields for many parts of m2o and m2m relations
	 * @var bool
	 */
	protected $_generateManyFields = false;

	/**
	 * Use this to override field types for columns. key = column, value = field type
	 * @var array
	 */
	protected $_fieldTypes = array();

	/**
	 * Field labels. key = column name, value = label
	 * @var array
	 */
	protected $_fieldLabels = array();

	/**
	 * Labels to use with many to many relations.
	 * key = related class name, value = label
	 * @var array
	 */
	protected $_relationLabels = array();

	/**
	 * Model instance for editing existing models
	 * @var Doctrine_Record
	 */
	protected $_instance = null;

	/**
	 * Form PluginLoader
	 * @var Zend_Loader_PluginLoader
	 */
	protected $_formLoader = null;

	/**
	 * Stores form class names for many-relations
	 * @var array
	 */
	protected $_relationForms = array();

    /**
     * @return string
     */
    function getAction() { return $this->_action; }

    /**
     * @return string
     */
    function getMethod() { return $this->_method; }

    /**
     * @throws Exception
     * @param  $model
     * @param string $action
     * @param string $method
     */
    function __construct($model , $action = '' , $method = 'post') {
        parent::__construct();

        $this->_model = $model;
		if($this->_model == '')
			throw new Exception('No model defined');
		$this->_table = Doctrine::getTable($this->_model);

		$this->_generateForm();

        $this->_action = $action;
        $this->_method = $method;

        $this->assign('f',$this);
    }

    /**
	 * Get unignored columns
	 * @return array
	 */
    protected function _getColumns() {
		$columns = array();
		foreach($this->_table->getColumns() as $name => $definition)
		{
			if((isset($definition['primary']) && $definition['primary']) ||
				!isset($this->_columnTypes[$definition['type']]) || in_array($name, $this->_ignoreColumns))
				continue;
			$columns[$name] = $definition;
		}

		return $columns;
	}

    /**
	 * Returns all un-ignored relations
	 * @return array
	 */
    protected function _getRelations() {
		$relations = array();

		foreach($this->_table->getRelations() as $name => $definition)
		{
			if(in_array($definition->getLocal(), $this->_ignoreColumns) ||
				($this->_generateManyFields == false && $definition->getType() == Doctrine_Relation::MANY))
				continue;

			$relations[$name] = $definition;
		}

		return $relations;
	}

    /**
     * Parses relations to fields
     * @return void
     */
	protected function _relationsToFields()
	{
		foreach($this->_getRelations() as $alias => $relation)
		{
			$field = null;

			switch($relation->getType())
			{
			case Doctrine_Relation::ONE:
				$table = $relation->getTable();
				$idColumn = $table->getIdentifier();

                $options[] = "";
				foreach($table->findAll() as $row)
				{
                    $array = $row->toArray();
                    unset($array[$idColumn]);
					$options[$row->$idColumn] = implode(',',$array);
				}

				$field = $this->createElement('select', $this->_fieldPrefix . $relation->getLocal());
                $field->setLabel($alias);
                $field->setRelation( true );
				$field->setMultiOptions($options);

				break;
			}

			if($field != null)
				$this->addElement($field);
		}
	}

    /**
     * Parses columns to fields
     * @return void
     */
	protected function _columnsToFields()
	{
        foreach($this->_getColumns() as $name => $definition)
        {
            
            if(!$this->columnInRelations($name))
            {
                if(!is_null($definition['length']))
                    $type = $this->_columnTypes[$definition['type']];
                else
                    $type = 'textarea';

                if(isset($this->_fieldTypes[$name]))
                    $type = $this->_fieldTypes[$name];

                $field = $this->createElement($type, $this->_fieldPrefix . $name);
                $field->setRealType($definition['type']);
                $label = $name;
                if(isset($this->_fieldLabels[$name]))
                    $label = $this->_fieldLabels[$name];

                if(isset($this->_columnValidators[$definition['type']]))
                    $field->addValidation($this->_columnValidators[$definition['type']]);

                if(isset($definition['notnull']) && $definition['notnull'] == true)
                    $field->setRequired(true);

                $field->setLabel(ucfirst($label));

                if($type == 'select' && $definition['type'] == 'enum')
                {
                    foreach($definition['values'] as $text)
                    {
                        $field->addMultiOption($text, ucwords($text));
                    }
                }

                $this->addElement($field);
            }
        }
	}

    /**
     * @param  $element
     * @return void
     */
    private function addElement($element) { $this->_elements[] = $element; }

    /**
     * @return 
     */
    public function getElements() { return $this->_elements; }

    /**
     * @param  $column
     * @return bool
     */
    private function columnInRelations( $column ) {
        $relations = $this->_getRelations();
        foreach($relations as $name => $relation)
        {
            if($column == $relation->getLocal())
                return true;
        }
        return false;
    }

    /**
     * @param  $type
     * @param  $value
     * @return element
     */
    private function createElement( $type , $value ) { return new element( $type , $value ); }

	/**
     * Generates the form
     * @return void
     */
	protected function _generateForm()
	{
		$this->_columnsToFields();
		$this->_relationsToFields();
	}

    /**
	 * Set the model instance for editing existing rows
	 * @param Doctrine_Record $instance
	 */
	public function setRecord($instance)
	{
		$this->_instance = $instance;
		foreach($this->_getColumns() as $name => $definition)
		{
			$this->setDefault($this->_fieldPrefix . $name, $this->_instance->$name);
		}

		foreach($this->_getRelations() as $name => $relation)
		{
			switch($relation->getType())
			{
			case Doctrine_Relation::ONE:
				$idColumn = $relation->getTable()->getIdentifier();
				$this->setDefault($this->_fieldPrefix . $relation->getLocal(), $this->_instance->$name->$idColumn);
				break;
			}
		}
	}

    /**
     * @param  $name
     * @param  $value
     * @return void
     */
    private function setDefault( $name , $value ) { $this->defaults[$name] = $value; }

    /**
     * @param  $name
     * @return array
     */
    public function getDefault ($name) { return $this->defaults[$name]; }

    /**
     * @return Doctrine_Record|null
     */
	public function getRecord()
	{
		return ($this->_instance != null) ? $this->_instance : new $this->_model;
	}
    
}

/**
 * Created by Mauro Gadaleta
 * User: Mauro Gadaleta
 * Date: 28/04/11
 * Time: 12:22
 *
 * Clase Elemento de formulario
 */
class element {
    /**
     * @var 
     */
    protected $realType;

    /**
     * @var
     */
    protected $type;

    /**
     * @var
     */
    protected $value;

    /**
     * @var bool
     */
    protected $required = false;

    /**
     * @var
     */
    protected $label;

    /**
     * @var
     */
    protected $validation;

    /**
     * @var bool
     */
    protected $relation = false;

    /**
     * @var array
     */
    protected $options = array();

    /**
     * Constructor set up
     * @param  $type
     * @param  $value
     */
    function __construct( $type , $value ) {
        $this->type = $type;
        $this->value = $value;
    }

    /**
     * @return 
     */
    function getType() { return $this->type; }

    /**
     * @return 
     */
    function getValue() { return $this->value; }

    /**
     * @param  $type
     * @return void
     */
    function addValidation( $type ) { $this->validation = $type; }

    /**
     * @return 
     */
    function getValidation() { return $this->validation; }

    /**
     * @param  $key
     * @param  $value
     * @return void
     */
    function addMultiOption( $key , $value ) { $this->options[$key] = $value; }

    /**
     * @param  $options
     * @return void
     */
    function setMultiOptions( $options ) { $this->options = $options; }

    /**
     * @return array
     */
    function getMultiOptions() { return $this->options; }

    /**
     * @param  $bool
     * @return void
     */
    function setRequired( $bool ) { $this->required = $bool; }

    /**
     * @return bool
     */
    function getRequired() { return $this->required; }

    /**
     * @param  $label
     * @return void
     */
    function setLabel( $label ) { $this->label = $label; }

    /**
     * @return
     */
    function getLabel() { return $this->label; }

    /**
     * @param  $relation
     * @return void
     */
    function setRelation ( $relation ) { $this->relation = $relation; }

    /**
     * @return bool
     */
    function getRelation() { return $this->relation; }

    /**
     * @param  $realType
     * @return void
     */
    function setRealType ( $realType ) { $this->realType = $realType; }

    /**
     * @return 
     */
    function getRealType() { return $this->realType; }
}